package com.linzm.optional;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * @Author zimingl
 * @Date 2022/10/30 15:46
 */
public class OptionalTest {

    private static Logger log = LoggerFactory.getLogger(OptionalTest.class);

    /**
     * 由于公司严格的代码合规，需要减少if、if else、for、switch case、&&、||、三目运算符的连用所以用Optional减少if判断。
     * public final class Optional<T> extends Object 是一个容器对象，可以为null。如果值存在则isPresent()方法会返回true，
     * 调用get()方法会返回该对象。可以保持类型T的值，或者仅仅保持null。Optional不用显示进行控制检测，很好的解决了空指针异常。
     */

    /**
     * of方法测试
     * 返回一个指定Optional的非空值，如果是控制，会报NullPointException
     */
    @Test
    public void optionalTest01() {
        User u1 = new User();
        User u2 = new User();
        u2.setName("张三");
        User u3 = null;
        Optional<User> user1 = Optional.of(u1);
        Optional<User> user2 = Optional.of(u2);
        // 打印空user对象
        log.info("user1:" + user1.toString());
        // 打印name = 张三的user对象
        log.info("user2:" + user2.toString());
        // NullPointerException
        Optional<User> user3 = Optional.of(u3);
    }

    /**
     * ofNullable测试
     * 返回一个Optional描述的指定值，如果为空，则返回Optional
     */
    @Test
    public void optionalTest02() {
        User u1 = new User();
        User u2 = new User();
        u2.setName("张三");
        User u3 = null;
        Optional<User> u11 = Optional.ofNullable(u1);
        Optional<User> u12 = Optional.ofNullable(u2);
        Optional<User> u13 = Optional.ofNullable(u3);
        // 打印空user对象
        log.info("u11:" + u11.toString());
        // 打印name = 张三的user对象
        log.info("u12:" + u12.toString());
        // 打印empty
        log.info("u13:" + u13.toString());
        // 可以抛出自定义异常
        Optional.ofNullable(u3).orElseThrow(() -> new NullPointerException());
    }

    /**
     * get测试
     * 如果Optional存在一个值，则返回，不存在则抛出NoSuchElementException
     */
    @Test
    public void optionalTest03() {
        User u2 = new User();
        u2.setName("张三");
        User u3 = null;
        User user2 = Optional.ofNullable(u2).get();
        // 打印name = 张三的user对象
        log.info("user2:" + user2);
        // NoSuchElementException
        User user3 = Optional.ofNullable(u3).get();
        log.info("user3:" + user3);
    }

    /**
     * isPresent测试
     * 如果有值返回true，如果为null返回false
     */
    @Test
    public void optionalTest04() {
        User u2 = new User();
        u2.setName("张三");
        User u3 = null;
        boolean booleanUser2 = Optional.ofNullable(u2).isPresent();
        boolean booleanUser3 = Optional.ofNullable(u3).isPresent();
        // true
        log.info("booleanUser2:" + booleanUser2);
        // false
        log.info("booleanUser3:" + booleanUser3);
    }

    /**
     * ifPrensent测试
     * 该方法也可以检测是否为null，同时该方法还可以接受一个Consumer参数
     */
    @Test
    public void optionalTest05() {
        User u2 = new User();
        u2.setName("张三");
        Optional.ofNullable(u2).ifPresent(user -> user.setAge(21));
        // 21
        log.info("u2:" + u2.getAge());
    }

    /**
     * orElse测试
     *
     * 如果返回值存在则返回，否则返回other
     */
    @Test
    public void optionalTest06() {
        User u2 = new User();
        u2.setName("张三");
        User u3 = null;
        User user = Optional.ofNullable(u3).orElse(u2);
        // 打印u2对象
        log.info("user:" + user.toString());
    }

    /**
     * orElseGet测试
     * 如果有值则返回，没有则调用Supplier函数，并返回
     */
    @Test
    public void optionalTest07() {
        User u2 = new User();
        u2.setName("张三");
        User u3 = null;
        User user1 = Optional.ofNullable(u3).orElseGet(() -> new User());
        User user2 = Optional.ofNullable(u3).orElseGet(() -> u2);
        User user3 = Optional.ofNullable(u2).orElseGet(() -> u2);
        // 打印User空对象
        log.info("user1:" + user1.toString());
        // u2对象
        log.info("user2:" + user2.toString());
        log.info("user3:" + user3.toString());
    }

    /**
     * orElseThrow
     * 有值时返回该值，没有时则抛出异常
     */
    @Test()
    public void optionalTest08() {
        User u1 = null;
        User u2 = new User();
        u2.setName("张三");
        User user2 = Optional.ofNullable(u2).orElseThrow(() -> new IllegalStateException());
        // 打印u2对象
        log.info("user2:" + user2.toString());
        // IllegalStateException
        User user1 = Optional.ofNullable(u1).orElseThrow(() -> new IllegalStateException());
    }

    /**
     * map测试
     */
    @Test
    public void optionalTest09() {
        User u1 = new User();
        u1.setName("张三");
        User u2 = new User();
        u2.setName("李四");
        u2.setEmail("lisi@163.com");
        Optional<String> s = Optional.ofNullable(u1).map(user -> user.getEmail()); //  Optional.ofNullable(u1).map(User::getEmail);
        String email1 = s.orElse("zhangsan@sina.com");
        // zhangsan@sina.com
        log.info("email1:" + email1);
        String email2 = Optional.ofNullable(u2).map(user -> user.getEmail()).orElse("lisi@hotmail.com");
        // lisi@163.com
        log.info("email2:" + email2);
    }

    /**
     * flatMap
     */
    @Test
    public void optionalTest10() {
        User u1 = new User();
//        u1.setName("张三");
/*        String name2 = Optional.ofNullable(u1)
                .flatMap(user -> user.getName())
                .orElse("zhangsan");*/
        String name = Optional.ofNullable(u1)
                .flatMap(u -> {return Optional.ofNullable(u.getName());})
                .orElse("zhangsan");
        // 张三
        log.info("name:" + name);
    }

    /**
     * filter测试
     * 接受一个Predicate参数，返回测试结果为true的值。如果测试结果为false，会返回一个空的 Optional
     */
    @Test
    public void optionalTest11() {
        User user = new User();
        user.setName("张三");
        user.setAge(11);
        User user1 = Optional.of(user).filter(u -> u.getName().equals("张三") && u.getAge() == 11).orElseThrow(NullPointerException::new);
        // user对象
        log.info("user1:" + user1);
        // NullPointerException
        User user2 = Optional.of(user).filter(u -> u.getName().equals("张三") && u.getAge() == 22).orElseThrow(NullPointerException::new);
        log.info("user2:" + user2);
    }

    @Test
    public void optionalTest12(){
        PictureCondition pictureCondition = new PictureCondition();

/*        if(pictureCondition==null||pictureCondition.getUrl()==null||"".equals(pictureCondition.getUrl().trim())){
            throw new RuntimeException("出错啦");
        }*/

        Optional.ofNullable(pictureCondition)
                .map(PictureCondition::getUrl)
                .map(String::trim)
                .filter(x -> !"".equals(x))
                .orElseThrow(() -> new RuntimeException("出错啦"));
    }

    @Test
    public List optionalTest13(){
        User user = new User();
/*        if(user!=null && user.getOrders()!=null) {
            return user.getOrders();
        } else {
            return Collections.emptyList();
        }*/
        return Optional.ofNullable(user)
                .map(u -> u.getOrders())
                .orElse(Collections.emptyList());
    }

    @Test
    public String test14(){

        User user = null;
   /*     if(user != null) {
            String name = user.getUsername();
            if(name != null) {
                return name.toUpperCase();
            } else {
                return null;
            }
        } else {
            return null;
        }*/
        return Optional.ofNullable(user)
                .map(User::getUsername)
                .map(String::toUpperCase)
                .orElse(null);
    }

    /**
     * 这时就可以使用flatMap来解决这个问题，刚才已经说了，flatMap不会使用Optional包装Function执行的返回结果，所以我们可以使用flatMap来解决这个问题
     * @param school
     * @return
     */

/*    @Test
    public  String getStudentName(School school) {
        return Optional.ofNullable(school)
                .map(School::getTearch)
                .map(Tearch::getStudent)
                .map(Student::getName)
                .orElse("false");
    }*/

    @Test
    public String getStudentName2(School school){
        return Optional.ofNullable(school)
                .flatMap(School::getTearch)
                .flatMap(Tearch::getStudent)
                .map(Student::getName).orElse("false");
    }

    @Test
    public void test15(){
        Optional<String> value = Optional.ofNullable("value");
        log.info(value.map(OptionalTest::getOutput).toString());
        log.info(value.flatMap(OptionalTest::getOutputOpt).toString());
    }

    static Optional<String> getOutputOpt(String input) {
        return input == null ? Optional.empty() : Optional.of("output for " + input);
    }

    static String getOutput(String input) {
        return input == null ? null : "output for " + input;
    }
}
